#include <linux/bitmap.h>
#include <linux/kthread.h>
#include <linux/proc_fs.h>
#include <linux/mutex.h>
#include <linux/seq_file.h>
#include <asm/uaccess.h>
#include <linux/fs.h>
#include <linux/mm.h>
#include <linux/time.h>
#include <linux/slab.h>
#include <linux/ctype.h>
#include <linux/types.h>
#include <linux/string.h>
#include <linux/module.h>
#include <linux/parser.h>
#include <linux/kernel.h>
#include <linux/seq_file.h>
#include <linux/backing-dev.h>
#include <linux/atomic.h>
#include "mtools.h"


#define ADPM_DEBUG 1

#if ADPM_DEBUG
#define adpm_pr(fmt, ...) printk(KERN_ALERT "ADPM_PRINT "pr_fmt(fmt), ##__VA_ARGS__)
#else
#define adpm_pr(fmt, ...) ((void)(0))
#endif




#define RSEC_CQ_DEPTH 128
#define MAX_IB_SGE_SIZE 4
#define MAX_INLINE 256
#define PSN 3185

#define STR_LEN 64

#define TEST_WRITE 0
#define TEST_READ 1
#define TEST_SEND 3
#define UNUSED(x) (void)(x)


struct Input_params {
  int base_port_index;
  int device_id;
  char memcache_ip[STR_LEN];
  int memcache_port;
  int blk_size;
  char test_mode[STR_LEN];
  int iter_num;
};

struct kib_inf {
  struct Input_params inp;
  struct MemcInfo mi;
};

spinlock_t kIB_param_lock; //the param lock
static struct Input_params kIB_param = {
  .base_port_index = 1,
  .device_id = 0,
  .memcache_ip = "127.0.0.1",
  .memcache_port = 1111,
  .blk_size = 4096,
  .test_mode = "write",
  .iter_num = 0,
};



static struct task_struct *main_handler;



static int adpm_net_proc_show(struct seq_file *f, void *v)
{
  seq_printf(f, "ib port index          %d\n", kIB_param.base_port_index);
  seq_printf(f, "device id:             %d\n", kIB_param.device_id);
  seq_printf(f, "memcache ip            %s\n", kIB_param.memcache_ip);
  seq_printf(f, "memcache port          %d\n", kIB_param.memcache_port);
  seq_printf(f, "block size             %d\n", kIB_param.blk_size);
  seq_printf(f, "test mode              %s\n", kIB_param.test_mode);
  seq_printf(f, "iterator number        %d\n", kIB_param.iter_num);
  return 0;
}
static int adpm_net_proc_open(struct inode *inode, struct file *file)
{
  return single_open(file, adpm_net_proc_show, NULL);
}
enum {
  //param
  Opt_ibport, Opt_devid, Opt_memcip, Opt_memcport, 
  Opt_blksiz, Opt_tmode, Opt_iternum, 
  //control
  Opt_start, Opt_set, Opt_get, Opt_err,
};
static const match_table_t tokens = {
  // param
  {Opt_ibport, "ibport=%d"}, {Opt_devid, "devid=%d"},
  {Opt_memcip, "mip=%s"}, {Opt_memcport, "mport=%d"},
  {Opt_blksiz, "blk=%d"}, {Opt_tmode, "mode=%s"}, 
  {Opt_iternum, "iter=%d"}, 
  // control
  {Opt_start, "start"}, {Opt_set, "set=%s"},
  {Opt_get ,"get=%s"},
  {Opt_err, NULL},
};
static ssize_t adpm_net_proc_write(struct file *file, const char __user *buf,
				size_t count, loff_t *offs)
{
  char *ops, *p, *pp;
  int res, len;
  substring_t args[MAX_OPT_ARGS];

  if(count >= STR_LEN){
    pr_err("adpm net proc write bytes too long\n");
    return -EINVAL;
  }
  ops = kzalloc(count, GFP_KERNEL);
  if(!ops)
    return -ENOMEM;
  pp=ops;
  if(copy_from_user(ops, buf, count)){
    kfree(pp);
    return -EFAULT;
  }
  if(ops[count - 1] == '\n')ops[count - 1] = '\0';
  spin_lock(&kIB_param_lock);
  while((p = strsep(&ops, ",")) != NULL){
    int token;
    if(!*p)continue;
    token = match_token(p, tokens, args);
    switch (token)
    {
    case Opt_ibport:
      if(match_int(&args[0], &res) || res < 0){
        pr_err("ibport parse wrong\n");
        continue;
      }
      kIB_param.base_port_index = res;
      break;
    case Opt_devid:
      if(match_int(&args[0], &res) || res < 0){
        pr_err("devid parse wrong\n");
        continue;
      }
      kIB_param.device_id = res;
      break;
    case Opt_memcip:
      len = args[0].to-args[0].from;
      strncpy(kIB_param.memcache_ip, args[0].from, len);
      kIB_param.memcache_ip[len] = '\0';
      break;
    case Opt_memcport:
      if(match_int(&args[0], &res) || res < 0){
        pr_err("memport parse wrong\n");
        continue;
      }
      kIB_param.memcache_port = res;
      break;
    case Opt_blksiz:
      if(match_int(&args[0], &res) || res < 0){
        pr_err("block size parse wrong\n");
        continue;
      }
      kIB_param.blk_size = res;
      break;
    case Opt_iternum:
      if(match_int(&args[0], &res) || res < 0){
        pr_err("iternum parse wrong\n");
        continue;
      }
      kIB_param.iter_num = res;
      break;
    case Opt_tmode:
      len = args[0].to - args[0].from;
      strncpy(kIB_param.test_mode, args[0].from, len);
      kIB_param.test_mode[len] = '\0';
      break;
    case Opt_start:
      wake_up_process(main_handler);
      break;
    case Opt_set:
      {
        struct MemcInfo mi;
        int ret;
        char *val;
        mt_create_info(&mi, kIB_param.memcache_ip, kIB_param.memcache_port);
        val = args[0].from;
        while(val < args[0].to){
          if(*val == ' ')break;
          ++val;
        }
        if(val == args[0].to){
          pr_err("ERROR: set format wrong\n");
          break;
        }
        *(val++) = '\0';
        len = args[0].to - val;
        ret = mt_set(&mi, args[0].from, val, len);
        if(ret)pr_err("ERROR: %s memcached set wrong\n", __FUNCTION__);
      }
      break;
    case Opt_get:
      {
        struct MemcInfo mi;
        char *val;
        int ret;
        val = kzalloc(STR_LEN, GFP_KERNEL);
        if(!val){
          pr_err("ERROR: %s memcache get no memory\n", __FUNCTION__);
          break;
        }
        mt_create_info(&mi, kIB_param.memcache_ip, kIB_param.memcache_port);
        *args[0].to = '\0';
        ret = mt_get(&mi, args[0].from, val, STR_LEN);
        if(ret == 1){
          adpm_pr("%s key no found\n", __FUNCTION__);
        }else if(ret){
          pr_err("ERROR: %s mt get wrong\n", __FUNCTION__);
        }else{
          adpm_pr("get value %s\n", val);
        }
        kfree(val);
      }
      break;
    default:
      pr_err("ERROR: unkown parse wrong\n");
      break;
    }
  }
  spin_unlock(&kIB_param_lock);
  kfree(pp);
  return count;
}

static const struct file_operations adpm_net_proc_fops = {
	.open		=	adpm_net_proc_open,
	.read		=	seq_read,
	.write		=	adpm_net_proc_write,
	.llseek		=	seq_lseek,
	.release	=	single_release
};

int main_thread(void *data)
{
  struct kib_inf *inf;
  int ret;

  inf = kzalloc(sizeof(struct kib_inf), GFP_KERNEL);
  if(!inf){
    pr_err("ERROR: no mem for inf\n");
    return -ENOMEM;
  }
  // main while
  for(;;){
    set_current_state(TASK_INTERRUPTIBLE);
    adpm_pr("main thread new cycle begin\n");
    schedule();   
    if(kthread_should_stop()){
      adpm_pr("main thread stoped\n");
      break;
    }
    adpm_pr("main thread schedule finish, work start\n");
    
    memset(inf, 0, sizeof(struct kib_inf));
    spin_lock(&kIB_param_lock);
    memcpy(&inf->inp, &kIB_param, sizeof(struct Input_params));
    spin_unlock(&kIB_param_lock);

    mt_create_info(&inf->mi, inf->inp.memcache_ip, inf->inp.memcache_port);
    
  }

  if(inf)kfree(inf);
  return ret;
}

static int adpm_net_init(void)
{
  int ret;
  main_handler = NULL;
  spin_lock_init(&kIB_param_lock);
  //create the proc file
  proc_create("adpm_net", 0, NULL, &adpm_net_proc_fops);
  //running worker thread
  main_handler = kthread_run(main_thread, NULL, "adpm_net_thread");
  if(IS_ERR(main_thread)){
    pr_err("admp net main thread wrong\n");
    remove_proc_entry("adpm_net", NULL);
    return -EIO;
  }

  return 0;
}
static void adpm_net_exit(void)
{
  if(main_handler)kthread_stop(main_handler);
  remove_proc_entry("adpm_net", NULL);
}

module_init(adpm_net_init);
module_exit(adpm_net_exit);
MODULE_LICENSE("GPL");
